/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2010 University College London 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Joao Taveira Araujo <j.araujo@ucl.ac.uk>
 */

#ifndef IPV4_REACT_ROUTING_H
#define IPV4_REACT_ROUTING_H

#include <list>
#include <utility>
#include "ns3/ipv4-address.h"
#include "ns3/ipv4-header.h"
#include "ns3/socket.h"
#include "ns3/ptr.h"
#include "ns3/ipv4.h"
#include "ns3/ipv4-routing-protocol.h"
#include "ns3/random-variable.h"
#include "ipv4-react-table-entry.h"

namespace ns3 {

class Packet;
class NetDevice;
class Ipv4Interface;
class Ipv4Address;
class Ipv4Header;
class Ipv4ReactTableEntry;
class Ipv4MulticastRoutingTableEntry;
class Node;

/**
 * \ingroup routing
 * \defgroup ipv4ReactRouting Ipv4ReactRouting
 */
/**
 * \ingroup ipv4ReactRouting
 * 
 * \brief React routing protocol for IP version 4 stacks.
 *
 */
class Ipv4ReactRouting : public Ipv4RoutingProtocol
{
public:
  static TypeId GetTypeId (void);
  static const uint32_t ROUTE_MAX = 4;

  Ipv4ReactRouting ();
  virtual ~Ipv4ReactRouting ();

  virtual Ptr<Ipv4Route> RouteOutput (Ptr<Packet> p, const Ipv4Header &header, Ptr<NetDevice> oif, Socket::SocketErrno &sockerr);

  virtual bool RouteInput  (Ptr<const Packet> p, const Ipv4Header &header, Ptr<const NetDevice> idev,
                             UnicastForwardCallback ucb, MulticastForwardCallback mcb,
                             LocalDeliverCallback lcb, ErrorCallback ecb);

  virtual void NotifyInterfaceUp (uint32_t interface);
  virtual void NotifyInterfaceDown (uint32_t interface);
  virtual void NotifyAddAddress (uint32_t interface, Ipv4InterfaceAddress address);
  virtual void NotifyRemoveAddress (uint32_t interface, Ipv4InterfaceAddress address);
  virtual void SetIpv4 (Ptr<Ipv4> ipv4);

                                                
/**
 * \brief Check if interface is outgoing from network domain.
 *
 * \param interface Interface number
 *
 * \see Ipv4Address
 */                                              
  bool IsExternalInterface (uint32_t ifnum);
                                                                    
  Ptr<Ipv4ReactTableEntry> AddDefaultNetworkRouteTo (Ipv4Address network, 
                                            Ipv4Mask networkMask, 
                                            uint32_t interface);
/**
 * \brief Add a network route to the static routing table.
 *
 * \param network The Ipv4Address network for this route.
 * \param networkMask The Ipv4Mask to extract the network.
 * \param interface The network interface index used to send packets to the
 * destination.
 * \param def Whether route should be added to default routing table
 *
 * \see Ipv4Address
 */
  Ptr<Ipv4ReactTableEntry> AddNetworkRouteTo (Ipv4Address network, 
                                     Ipv4Mask networkMask, 
                                     uint32_t interface,
                                     bool def = false);

/**
 * \brief Get the number of individual unicast routes that have been added
 * to the routing table.
 *
 * \warning The default route counts as one of the routes.
 */
  uint32_t GetNRoutes (void);

/**
 * \brief Get a route from the static unicast routing table.
 *
 * Externally, the unicast static routing table appears simply as a table with
 * n entries.  
 *
 * \param i The index (into the routing table) of the route to retrieve.  
 * \return If route is set, a pointer to that Ipv4ReactTableEntry is returned, otherwise
 * a zero pointer is returned.
 *
 * \see Ipv4ReactTableEntry
 * \see Ipv4ReactRouting::RemoveRoute
 */
  Ptr<Ipv4ReactTableEntry> GetRoute (uint32_t i);

/**
 * \brief Get a metric for route from the static unicast routing table.
 *
 * \param index The index (into the routing table) of the route to retrieve.  
 * \return If route is set, the metric is returned. If not, an infinity metric (0xffffffff) is returned
 *
 */
  uint32_t GetMetric (uint32_t index);
  
/**
 * \brief Remove a route from the static unicast routing table.
 *
 * Externally, the unicast static routing table appears simply as a table with
 * n entries.  
 *
 * \param i The index (into the routing table) of the route to remove.  
 *
 * \see Ipv4ReactTableEntry
 * \see Ipv4ReactRouting::GetRoute
 * \see Ipv4ReactRouting::AddRoute
 */
  void RemoveRoute (uint32_t i);

  void UpdateLexStats();
  void UpdatePrefStats();
  void UpdatePref(Ipv4Address *entry);
 
protected:
  virtual void DoDispose (void);
                     
private:                                    
  typedef std::vector<Ptr<Ipv4ReactTableEntry> > RouteTable;
  typedef std::vector<Ptr<Ipv4ReactTableEntry> >::const_iterator RouteTableCI;
  typedef std::vector<Ptr<Ipv4ReactTableEntry> >::iterator RouteTableI;

  Ptr<Ipv4ReactTableEntry> LookupReact (Ipv4Address dest, RouteTable* routeTable, uint32_t interface=0);
  Ptr<Ipv4ReactTableEntry> LookupReact (Ipv4Address dest, uint8_t path = 0);
  void RemoveRoute ( RouteTable* table, Ptr<Ipv4ReactTableEntry> entry);
  uint32_t SelectRoute (Ipv4Address dest);
  Ptr<Ipv4> m_ipv4;                                                  

  RouteTable m_routeList; //all routes
  RouteTable m_defaultRoutes; //default routes only
  std::list<uint32_t> m_externalInterfaces;

  Time m_updatePrefInterval;
  Time m_updateLexInterval;

  double m_flowSplitMin;
  //int m_balancingMode;
  TracedCallback<Ptr<Ipv4ReactTableEntry> > m_traceUpdate;

  Callback <double, std::vector<Ptr<Ipv4ReactTableEntry> > *, double, double> m_flowSplitUpdate;
};


} // Namespace ns3

#endif /* IPV4_REACT_ROUTING_H */
